﻿using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ServerSidePhysics.Core
{
    public static class Physics
    {
        public static List<PhysicsClient> clients = new List<PhysicsClient>();
        public static World pWorld;
        public static Timer timer;

        public enum Shapetype
        {
            Rectangle,
            Circle
        }

        public struct BodyData
        {
            public Shapetype sType;
            public Vector2 postition;
            public float width;
            public float height;
            public float radius;
            public float density;

            public static BodyData Create(Shapetype stype, Vector2 pos, float Width = 0f, float Height = 0f, float Radius = 0f, float Density = 1f)
            {
                if (stype == Shapetype.Rectangle)
                    return new BodyData { sType = stype, postition = pos, width = Width, height = Height, density = Density };
                else
                    return new BodyData();
            }
        }

        public static void Initialize()
        {
            StaticHelpers.Log("Initializing Farseer Physics...");

            CreateWorld();
            AddShape(BodyData.Create(Shapetype.Rectangle, new Vector2(10f, 10f), 5f, 5f, Density: 1f));
            AddShape(BodyData.Create(Shapetype.Rectangle, new Vector2(200f, 200f), 5f, 5f, Density: 1f));

            timer = new Timer(new TimerCallback(Update), null, 0, (long)1000);
        }

        public static void AddShape(BodyData bd)
        {
            switch(bd.sType)
            {
                case Shapetype.Rectangle:
                    Body temp = BodyFactory.CreateRectangle(pWorld, bd.width, bd.height, bd.density);
                    temp.SetTransform(bd.postition, 0f);
                    temp.BodyType = BodyType.Dynamic;
                    break;
            }
        }

        public static byte[] CollectData()
        {
            Dictionary<int, Vector2> list = new Dictionary<int, Vector2>();

            foreach (Body b in Physics.pWorld.BodyList)
            {
                list.Add(b.BodyId, b.Position);
            }

            byte[] res = StaticHelpers.GetBodyArray(list);

            return res;
        }

        public static void BroadcastUpdate()
        {
            byte[] data = CollectData();

            if(clients.Count < 1)
            {
                return;
            }

            foreach(PhysicsClient pc in clients)
            {
                if (pc.handshaken == false)
                    continue;

                byte[] resp = pc.BuildResponse(data, PhysicsClient.OPCodes.worldUpdateOpcode);
                pc.SendByteResponse(resp, false, true, true);
            }
        }

        public static void Update(Object stateInfo)
        {
            pWorld.Step(50);
            BroadcastUpdate();

            //StaticHelpers.Log(pWorld.BodyList[0].Position.ToString());
        }

        public static void CreateWorld()
        {
            Vector2 gravity = new Vector2(0f, -9.82f);
            StaticHelpers.Log(String.Format("Creating physics World...({0}", gravity.ToString()));

            pWorld = new World(gravity);
        }

        public static void AddClient(PhysicsClient pc)
        {
            clients.Add(pc);

            StaticHelpers.Log("Client created!");
        }
    }
}
